"""
MySQL数据库连接工具类
用于测试中连接和操作MySQL数据库
"""
from typing import Dict, List, Any, Optional
from urllib.parse import quote_plus
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from sqlalchemy.exc import SQLAlchemyError
from utils.logger_util import logger


class DatabaseUtil:
    """MySQL数据库操作工具类"""
    
    def __init__(self, username: str, password: str, host: str = "127.0.0.1", 
                 port: int = 3306, database: str = "database_name"):
        # 初始化工具：保存连接配置，并立刻尝试连接数据库，方便后续直接使用
        """
        初始化MySQL数据库连接
        
        Args:
            username: 数据库用户名
            password: 数据库密码
            host: 数据库主机地址，默认127.0.0.1
            port: 数据库端口，默认3306
            database: 数据库名，默认lottery_system
        """
        self.log = logger.getlog()
        self.username = username
        self.password = password
        self.host = host
        self.port = port
        self.database = database
        self.engine = None
        self.session_factory = None
        self._connect()
    
    def _connect(self):
        # 实际建立数据库连接（创建引擎和连接池，并准备会话工厂）
        """建立MySQL数据库连接"""
        try:
            # 对用户名和密码进行URL编码，处理特殊字符
            encoded_username = quote_plus(str(self.username))
            encoded_password = quote_plus(str(self.password))
            
            # 拼接连接字符串（使用 PyMySQL 驱动 + UTF-8 编码）
            connection_string = (
                f"mysql+pymysql://{encoded_username}:{encoded_password}@"
                f"{self.host}:{self.port}/{self.database}"
                f"?charset=utf8&ssl_disabled=true"
            )
            
            # 创建 SQLAlchemy 引擎：开启心跳（pool_pre_ping）、回收（pool_recycle），避免长连接失效
            self.engine = create_engine(
                connection_string,
                pool_pre_ping=True,
                pool_recycle=3600,
                echo=False
            )
            
            # 立即测试连接，确保连接字符串正确
            with self.engine.connect() as connection:
                connection.execute(text("SELECT 1"))
            
            # 创建会话工厂：后续通过它创建会话（session）来执行SQL
            self.session_factory = sessionmaker(bind=self.engine)
            self.log.info(f"MySQL数据库连接成功: {self.host}:{self.port}/{self.database}")
            
        except Exception as e:
            self.log.error(f"MySQL数据库连接失败: {str(e)}")
            raise
    
    def execute_query(self, sql: str, params: Optional[Dict] = None) -> List[Dict[str, Any]]:
        # 查询数据：执行 SELECT 等查询语句，返回每行是字典的列表
        """
        执行查询SQL语句
        
        Args:
            sql: SQL查询语句
            params: 查询参数字典
            
        Returns:
            查询结果列表，每行数据为字典格式
        """
        # 1) 打开一次会话（类似打开一个临时连接）
        session = self.session_factory()
        try:
            self.log.info(f"执行查询SQL: {sql}")
            if params:
                self.log.info(f"查询参数: {params}")
            
            # 2) 执行SQL（text可绑定参数，能有效避免拼接SQL带来的风险）
            result = session.execute(text(sql), params or {})
            
            # 3) 取出列名和所有行
            columns = result.keys()
            rows = result.fetchall()
            
            # 4) 把每一行转成字典，键是列名，值是该列的数据
            data = []
            for row in rows:
                row_dict = dict(zip(columns, row))
                data.append(row_dict)
            
            self.log.info(f"查询返回 {len(data)} 条记录")
            return data
            
        except SQLAlchemyError as e:
            self.log.error(f"查询执行失败: {str(e)}")
            raise
        finally:
            # 5) 关闭会话（非常重要，避免连接泄漏）
            session.close()
    
    def execute_update(self, sql: str, params: Optional[Dict] = None) -> int:
        # 更新数据：用于 INSERT/UPDATE/DELETE，返回受影响的行数
        """
        执行更新SQL语句（INSERT, UPDATE, DELETE）
        
        Args:
            sql: SQL更新语句
            params: 更新参数字典
            
        Returns:
            受影响的行数
        """
        # 打开会话
        session = self.session_factory()
        try:
            self.log.info(f"执行更新SQL: {sql}")
            if params:
                self.log.info(f"更新参数: {params}")
            
            # 执行更新语句
            result = session.execute(text(sql), params or {})
            # 提交事务（没有提交就不会真正写入数据库）
            session.commit()
            
            affected_rows = result.rowcount
            self.log.info(f"更新影响了 {affected_rows} 行")
            return affected_rows
            
        except SQLAlchemyError as e:
            # 失败回滚，保证数据一致性
            session.rollback()
            self.log.error(f"更新执行失败: {str(e)}")
            raise
        finally:
            # 关闭会话
            session.close()
    
    def get_table_count(self, table_name: str, condition: str = None) -> int:
        # 获取某张表（可带条件）的记录数量
        """
        获取表的记录数量
        
        Args:
            table_name: 表名
            condition: WHERE条件（可选）
            
        Returns:
            记录数量
        """
        # 先写出基础SQL
        sql = f"SELECT COUNT(*) as count FROM {table_name}"
        # 如果传了条件，就把 WHERE 追加上去
        if condition:
            sql += f" WHERE {condition}"
        
        result = self.execute_query(sql)
        return result[0]['count'] if result else 0
    
    def get_table_data(self, table_name: str, condition: str = None, 
                      order_by: str = None, limit: int = None) -> List[Dict[str, Any]]:
        # 读取整张表或部分数据（支持条件、排序、限制条数）
        """
        获取表数据
        
        Args:
            table_name: 表名
            condition: WHERE条件（可选）
            order_by: 排序字段（可选）
            limit: 限制返回行数（可选）
            
        Returns:
            表数据列表
        """
        sql = f"SELECT * FROM {table_name}"
        
        if condition:
            sql += f" WHERE {condition}"
        if order_by:
            sql += f" ORDER BY {order_by}"
        if limit:
            sql += f" LIMIT {limit}"
        
        return self.execute_query(sql)
    
    def verify_data_exists(self, table_name: str, condition: str) -> bool:
        # 快速判断数据是否存在（内部用 COUNT>0 来判断）
        """
        验证数据是否存在
        
        Args:
            table_name: 表名
            condition: WHERE条件
            
        Returns:
            数据是否存在
        """
        count = self.get_table_count(table_name, condition)
        exists = count > 0
        self.log.info(f"验证数据存在性 - 表: {table_name}, 条件: {condition}, 结果: {exists}")
        return exists
    
    def get_latest_record(self, table_name: str, order_column: str = 'id') -> Optional[Dict[str, Any]]:
        # 获取最新的一条记录（按某列倒序，默认按 id）
        """
        获取最新的一条记录
        
        Args:
            table_name: 表名
            order_column: 排序字段，默认为id
            
        Returns:
            最新记录的字典，如果没有记录则返回None
        """
        data = self.get_table_data(table_name, order_by=f"{order_column} DESC", limit=1)
        return data[0] if data else None
    
    def compare_data_before_after(self, table_name: str, condition: str = None) -> Dict[str, int]:
        # 统计当前记录数（可配合操作前后分别调用，用来比较变化）
        """
        比较操作前后的数据变化（需要在操作前后分别调用）
        
        Args:
            table_name: 表名
            condition: WHERE条件（可选）
            
        Returns:
            包含before和after计数的字典
        """
        count = self.get_table_count(table_name, condition)
        return {'count': count, 'table': table_name, 'condition': condition}
    
    def test_connection(self) -> bool:
        # 轻量自检：执行 SELECT 1，确认数据库能正常连通
        """
        测试数据库连接是否成功
        
        Returns:
            bool: 如果连接成功返回True，否则返回False
        """
        try:
            # 尝试获取一个连接并立即释放，以测试连接性（不做任何数据修改）
            with self.engine.connect() as connection:
                connection.execute(text("SELECT 1"))
            self.log.info("数据库连接测试成功！")
            return True
        except SQLAlchemyError as e:
            self.log.error(f"数据库连接测试失败: {str(e)}")
            return False

    def close(self):
        # 主动关闭连接池（例如在测试结束或程序退出前调用）
        """关闭数据库连接"""
        if self.engine:
            self.engine.dispose()
            self.log.info("数据库连接已关闭")

